from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
import pandas as pd
import datetime
import os
import platform
from lib.logger import TestLogger
from lib.report_generator import ReportGenerator
from config.config import SCREENSHOT_DIR, BROWSER_TYPE

class WebUIAutomation:
    def __init__(self, excel_path):
        self.logger = TestLogger().logger
        self.setup_driver()
        self.load_test_data(excel_path)
        self.setup_directories()
        self.initialize_test_data()
        self.base_url = None

    def setup_driver(self):
        if BROWSER_TYPE == 'firefox':
            self.driver = webdriver.Firefox()
        elif BROWSER_TYPE == 'chrome':
            self.driver = webdriver.Chrome()
        elif BROWSER_TYPE == 'edge':
            self.driver = webdriver.Edge()
        self.browser_info = self.driver.capabilities

    def load_test_data(self, excel_path):
        try:
            self.test_data = pd.read_excel(excel_path)
        except Exception as e:
            self.logger.error(f"加载测试数据失败: {str(e)}")
            raise

    def setup_directories(self):
        timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        self.screenshot_dir = os.path.join(SCREENSHOT_DIR, timestamp)
        os.makedirs(self.screenshot_dir, exist_ok=True)

    def initialize_test_data(self):
        self.test_results = []
        self.test_start_time = datetime.datetime.now()
        self.platform_info = {
            'os': platform.system(),
            'os_version': platform.version(),
            'python_version': platform.python_version()
        }

    def take_screenshot(self, test_case):
        try:
            timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
            filename = f"{test_case['测试用例编号']}_{test_case['步骤编号']}_{timestamp}.png"
            save_path = os.path.join(self.screenshot_dir, filename)
            self.driver.save_screenshot(save_path)
            relative_path = f"../screenshots/{os.path.basename(self.screenshot_dir)}/{filename}"
            # self.logger.info(f"截图已保存: {save_path}")
            return relative_path
        except Exception as e:
            self.logger.error(f"截图失败: {str(e)}")
            return None

    def execute_test_step(self, test_case):
        """执行测试步骤"""
        start_time = datetime.datetime.now()
        screenshot = None
        try:
            self.logger.info(f"开始执行测试用例 {test_case['测试用例编号']} 步骤 {test_case['步骤编号']}")

            if test_case['测试步骤关键字'] == '等待':
                result = self.execute_keyword(test_case, None)
            else:
                element = self.wait_for_element_presence(
                    test_case['定位方式'],
                    test_case['定位地址']
                )
                result = self.execute_keyword(test_case, element)

            # 无论成功与否都截图
            screenshot = self.take_screenshot(test_case)
            
            end_time = datetime.datetime.now()
            duration = end_time - start_time

            self.test_results.append({
                '测试用例编号': test_case['测试用例编号'],
                '步骤编号': test_case['步骤编号'],
                '测试步骤': test_case['测试步骤'],
                '执行结果': 'Pass',
                '错误信息': '',
                '截图': screenshot,
                '开始时间': start_time,
                '结束时间': end_time,
                '执行时长': duration
            })

            self.logger.info(f"✓ 测试用例 {test_case['测试用例编号']} 步骤 {test_case['步骤编号']} 执行成功")

        except Exception as e:
            end_time = datetime.datetime.now()
            duration = end_time - start_time
            error_msg = str(e)
            
            # 记录错误日志
            self.logger.error(f"✗ 测试用例 {test_case['测试用例编号']} 步骤 {test_case['步骤编号']} 执行失败: {error_msg}")
            
            # 如果之前没有截图，在失败时截图
            if not screenshot:
                screenshot = self.take_screenshot(test_case)

            self.test_results.append({
                '测试用例编号': test_case['测试用例编号'],
                '步骤编号': test_case['步骤编号'],
                '测试步骤': test_case['测试步骤'],
                '执行结果': 'Fail',
                '错误信息': error_msg,
                '截图': screenshot,
                '开始时间': start_time,
                '结束时间': end_time,
                '执行时长': duration
            })
            # 抛出异常，但不记录额外的错误日志
            raise

    def execute_keyword(self, test_case, element):
        """执行关键字操作"""
        keyword = test_case['测试步骤关键字']
        value = test_case['参数值']
        expected_result = test_case.get('预期结果', '')

        keyword_actions = {
            '点击': lambda: element.click(),
            '输入': lambda: element.send_keys(value),
            '清除': lambda: element.clear(),
            '双击': lambda: ActionChains(self.driver).double_click(element).perform(),
            '右击': lambda: ActionChains(self.driver).context_click(element).perform(),
            '悬停': lambda: ActionChains(self.driver).move_to_element(element).perform(),
            '获取文本': lambda: element.text,
            '获取属性': lambda: element.get_attribute(value),
            '按键': lambda: element.send_keys(getattr(Keys, value)),
            '提交': lambda: element.submit(),
            '判断是否显示': lambda: element.is_displayed(),
            '判断是否启用': lambda: element.is_enabled(),
            '判断是否选中': lambda: element.is_selected(),
            '切换框架': lambda: self.driver.switch_to.frame(element),
            '返回默认框架': lambda: self.driver.switch_to.default_content(),
            '切换窗口': lambda: self.switch_window(value),
            '执行JS': lambda: self.driver.execute_script(value, element),
            '等待': lambda: self.wait_sleep(value),
            '等待元素可见': lambda: self.wait_for_element(test_case['定位方式'], test_case['定位地址'], float(value or 10)),
            '等待元素可点击': lambda: self.wait_for_element_clickable(test_case['定位方式'], test_case['定位地址'],
                                                               float(value or 10)),
            '等待元素存在': lambda: self.wait_for_element_presence(test_case['定位方式'], test_case['定位地址'],
                                                             float(value or 10)),
            '等待文本出现': lambda: self.wait_for_text_present(test_case['定位方式'], test_case['定位地址'],
                                                         expected_result, float(value or 10)),
        }

        if keyword in keyword_actions:
            return keyword_actions[keyword]()
        else:
            raise ValueError(f"不支持的关键字操作: {keyword}")

    def wait_for_element_presence(self, by, value, timeout=10):
        """等待元素存在"""
        by_dict = {
            'id': By.ID,
            'name': By.NAME,
            'class': By.CLASS_NAME,
            'tag': By.TAG_NAME,
            'link': By.LINK_TEXT,
            'plink': By.PARTIAL_LINK_TEXT,
            'xpath': By.XPATH,
            'css': By.CSS_SELECTOR
        }
        
        locator = (by_dict.get(by.lower(), By.XPATH), value)
        return WebDriverWait(self.driver, timeout).until(
            EC.presence_of_element_located(locator)
        )

    def wait_for_element(self, by, value, timeout=10):
        """等待元素可见"""
        locator = (getattr(By, by.upper()), value)
        return WebDriverWait(self.driver, timeout).until(
            EC.visibility_of_element_located(locator)
        )

    def wait_for_element_clickable(self, by, value, timeout=10):
        """等待元素可点击"""
        locator = (getattr(By, by.upper()), value)
        return WebDriverWait(self.driver, timeout).until(
            EC.element_to_be_clickable(locator)
        )

    def wait_for_text_present(self, by, value, text, timeout=10):
        """等待文本出现"""
        locator = (getattr(By, by.upper()), value)
        return WebDriverWait(self.driver, timeout).until(
            EC.text_to_be_present_in_element(locator, text)
        )

    def wait_sleep(self, seconds):
        """强制等待"""
        import time
        time.sleep(float(seconds))

    def switch_window(self, window_index):
        """切换窗口"""
        handles = self.driver.window_handles
        if window_index == '最新':
            self.driver.switch_to.window(handles[-1])
        elif window_index.isdigit() and int(window_index) < len(handles):
            self.driver.switch_to.window(handles[int(window_index)])

    def set_base_url(self, url):
        """设置测试目标URL"""
        self.base_url = url
        self.logger.info(f"设置测试目标URL: {url}")
        try:
            self.driver.get(url)
            self.logger.info("成功打开目标网页")
        except Exception as e:
            self.logger.error(f"打开目标网页失败: {str(e)}")
            raise

    def run_tests(self):
        """运行测试"""
        try:
            if not self.base_url:
                raise ValueError("未设置测试目标URL")

            self.test_start_time = datetime.datetime.now()
            
            for _, test_case in self.test_data.iterrows():
                try:
                    self.execute_test_step(test_case)
                except Exception as e:
                    # 错误已经在 execute_test_step 中记录，这里不需要重复记录
                    continue
                
            self.test_end_time = datetime.datetime.now()
            
            try:
                # 生成报告
                report_generator = ReportGenerator(
                    self.test_results,
                    self.test_start_time,
                    self.test_end_time
                )
                report_file = report_generator.generate_report()
                self.logger.info(f"测试报告已生成: {report_file}")
            except Exception as e:
                self.logger.error(f"生成测试报告失败: {str(e)}")
                raise
            
        except Exception as e:
            self.logger.error(f"测试执行失败: {str(e)}")
            raise
        finally:
            try:
                self.driver.quit()
            except:
                pass 